Title Banner

Previous Book Contents Book Index Next

Inside Macintosh: QuickTime Components /
Chapter 4 - Image Compressor Components


Using Image Compressor Components

This section shows how to use compressors and decompressors in conjunction with the Image Compression Manager.

Performing Image Compression

This section describes what the Image Compression Manager does that affects compressors. It then provides sample code that shows how the compressor components prepare for image compression and how to compress an entire image or a horizontal band of an image.

When compressing an image, the Image Compression Manager performs three
major tasks:

  1. The Image Compression Manager first determines which compressor is best able to compress the image. To do so, the Image Compression Manager examines the source image as well as the parameters specified by the application. If the application requested a specific compressor, the Image Compression Manager uses that compressor (unless it is not installed, in which case the Image Compression
    Manager returns an error to the application). If the application did not request a compressor, the Image Compression Manager chooses the compressor that will do the best job. The Image Compression Manager collects the information it needs to choose a compressor by issuing the CDPreCompress request to each qualifying compressor (see page 4-62 for a detailed description of the CDPreCompress function).
  2. If the chosen compressor can handle the image directly, the Image Compression Manager passes the request through to the compressor. The compressor then processes the image and returns the compressed data to the specified location.
  3. If none of the compressors can handle it directly, the Image Compression Manager allocates an offscreen buffer and passes image bands to the compressor by issuing a CDBandCompress request. (For more on the CDBandCompress function, see page 4-63.) The compressor processes each band, accumulating the compressed data as it goes. When the image has been completely compressed, the Image Compression Manager returns control to the application.

Choosing a Compressor

Listing 4-1 on page 4-12 shows how the Image Compression Manager calls the CDPreCompress function before an image is compressed. The compressor component returns information about how it is able to compress the image to the Image Compression Manager, so that it can fit the destination data to the requirements of
the compressor component. This information includes compressor capabilities for

When your compressor component is called with the CDPreCompress function (described on page 4-62), it can handle all aspects of the function itself, or only the most common ones. All image compressor components must handle at least one case.

Here is a list of some of the operations your compressor component can perform during compression. It describes parameters in the compression parameters structure (described on page 4-40) and indicates the operations that are required and which flags in the compressor capabilities flags field of the compressor capabilities structure (described on page 4-35) must be set to allow your compressor to handle them.

Listing 4-1 Preparing for simple compression operations

pascal long
CDPreCompress (Handle storage, register CodecCompressParams *p)
{
   CodecCapabilities *capabilities = p->capabilities;
/* 
   First the compressor returns which depth input pixels it
   supports based on what the application has available. This
   compressor can only work with 32-bit input pixels. 
*/    
   switch ( (*p->imageDescription)->depth )  {
      case 16:
         capabilities->wantedPixelSize = 32;
         break;
      default:
         return(codecConditionErr);
         break;
   }
   /* 
      If the buffer gets banded, return the smallest one the 
      compressor can handle. 
   */ 
   capabilities->bandMin = 2;
   /* 
      If the buffer gets banded, return the increment 
      by which it should increase. 
   */
   capabilities->bandInc = 2;
      
   capabilities->extendWidth = (*p->imageDescription)->width & 1;
   capabilities->extendHeight = (*p->imageDescription)->height &                                  1;

/* 
   For efficiency, if the compressor could perform extension,
   these flags would be set to 0. 
*/
   
   return(noErr);
}

Compressing a Horizontal Band of an Image

Listing 4-2 shows how the Image Compression Manager calls the CDBandCompress function when it wants the compressor to compress a horizontal band of an image.

Note
This example does not perform compression on bands with a bit depth of more than 1 or an extension of width and height. If the example did do so, it would handle these cases faster.
Listing 4-2 Performing simple compression on a horizontal band of an image

pascal long
CDBandCompress (Handle storage, register CodecCompressParams *p)
{
   short             width,height;
   Ptr               cDataPtr,dataStart;
   short             depth;
   Rect              sRect;
   long              offsetH,offsetV;
   Globals           **glob  = (Globals **)storage;
   register char     *baseAddr;
   long              numLines,numStrips;
   short             rowBytes;
   long              stripBytes;
   char              mmuMode = 1;
   register short    y;
   ImageDescription  **desc = p->imageDescription;
   OSErr             result = noErr;
   
   /* 
   If there is a progress function, give it an open call at
      the start of this band. 
   */
   if (p->progressProcRecord.progressProc)
      p->progressProcRecord.progressProc (codecProgressOpen, 0,
         p->progressProcRecord.progressRefCon);

   width = (*desc)->width;
   height = (*desc)->height;
   depth = (*desc)->depth;
   dataStart = cDataPtr = p->data;

   /* 
      Figure out offset to first pixel in baseAddr from the
      pixel size and bounds.
    */

   rowBytes = p->srcPixMap.rowBytes;
   sRect =  p->srcPixMap.bounds;
   numLines = p->stopLine - p->startLine; /* number of scan 
                                             lines */
   numStrips = (numLines+1)>>1;           /* number of strips 
                                             in */
   stripBytes = ((width+1)>>1) * 5;
   
   /* 
      Adjust the source baseAddress to be at the beginning 
      of the desired rect. 
   */

   switch ( p->srcPixMap.pixelSize ) {
   case 32:
      offsetH = sRect.left<<2;
      break;
   case 16:
      offsetH = sRect.left<<1;
      break;
   case 8:
      offsetH = sRect.left;
      break;

   /* 
      This compressor does not handle the other cases directly. 
   */

   default:
      result = codecErr;
      goto bail;
   }

   offsetV = sRect.top * rowBytes;
   baseAddr = p->srcPixMap.baseAddr + offsetH + offsetV;

   /* 
      If there is not a data-unloading function, 
      adjust the pointer to the next band. 
   */
   
   if (  p->flushProcRecord.flushProc == nil ) {
      cDataPtr += (p->startLine>>1) * stripBytes;
   }
   else { /* 
             Make sure the compressor can deal with the
             data-unloading function in this case. 
         */
      if ( p->bufferSize < stripBytes ) {
         result = codecSpoolErr;
         goto bail;
      }
   }
   /* 
      Perform the slower data-loading or progress operation, as
      required.
   */
   
   if (  p->flushProcRecord.flushProc  || 
      p->progressProcRecord.progressProc ) {

      SharedGlobals *sg = (*glob)->sharedGlob;

      for ( y=0; y < numStrips; y++) {
         SwapMMUMode(&mmuMode);
         CompressStrip(cDataPtr,baseAddr,rowBytes,width,sg);
         SwapMMUMode(&mmuMode);
         baseAddr += rowBytes<<1;
         if ( p->flushProcRecord.flushProc ) { 
            if ( (result=
         p->flushProcRecord.flushProc(cDataPtr,stripBytes,
         p->flushProcRecord.flushRefCon)) != noErr) {
               result = codecSpoolErr;
               goto bail;
            }
         } else {
            cDataPtr += stripBytes;
         }
         if (p->progressProcRecord.progressProc) {
            if ( (result=
               p->progressProcRecord.progressProc)
                  codecProgressUpdatePercent,
                  FixDiv(y,numStrips),
                  p->progressProcRecord.progressRefCon)
            )  != noErr ) {
               result = codecAbortErr;
               goto bail;
            }
         }
      }
   } else {
      SharedGlobals *sg = (*glob)->sharedGlob;
      short tRowBytes = rowBytes<<1;

      SwapMMUMode(&mmuMode);
      for ( y=numStrips; y--; ) {
         CompressStrip(cDataPtr,baseAddr,rowBytes,width,sg);
         cDataPtr += stripBytes;
         baseAddr += tRowBytes;
      }
      SwapMMUMode(&mmuMode);
   }
}

Decompressing an Image

When decompressing an image, the Image Compression Manager performs these three major tasks:

  1. The Image Compression Manager first determines which decompressor is best able to decompress the image. To do so, the Image Compression Manager examines the source image as well as the parameters specified by the application. If the application requested a specific decompressor, the Image Compression Manager uses that decompressor (unless it is not installed, in which case the Image Compression Manager returns an error to the application). If the application did not request a decompressor, the Image Compression Manager chooses the decompressor that will do the best job. The Image Compression Manager collects the information it needs to choose a decompressor by issuing the CDPreDecompress request to each qualifying decompressor (see page 4-63 for a detailed description of the CDPreDecompress function).
  2. If the chosen decompressor can handle the image directly, the Image Compression Manager passes the request through to the decompressor. The decompressor then processes the image and returns the image to the specified location.
  3. If none of the decompressors can handle all of the conditions (matrix mapping, masking or matting, depth conversion, and so on) the Image Compression Manager allocates an offscreen buffer and passes image bands to the decompressor at a depth that the decompressor can handle by issuing a CDBandDecompress request. (For details on the CDBandDecompress function, see page 4-64). The decompressor processes each band, building the image as it goes. When the image has been completely decompressed, the Image Compression Manager returns control to the application.

Choosing a Decompressor

Listing 4-3 on page 4-20 provides an example of how a decompressor is chosen. The Image Compression Manager calls the CDPreDecompress function (described on page 4-63) before an image is decompressed. The decompressor returns information about how it can decompress an image. The Image Compression Manager can fit the destination pixel map to your decompressor's requirements if it is not able to support decompression to the destination directly. The capability information the decompressor returns includes

When your decompressor component is called with the CDPreDecompress function, it can handle all aspects of the call itself, or only the most common ones. All decompressors must handle at least one case.

This section contains a bulleted list of some of the operations your decompressor component can perform during the decompression operation. The list describes which parameters in the decompression parameters structure (described on page 4-46) indicate the operations are required and which flags in the flags field of the compressor capabilities structure (described on page 4-35) must be set to allow your decompressor to handle them.

For sequences of images the conditionFlags field in the decompression parameters structure can be used to determine which parameters may have changed since the last decompression operation. These parameters are also indicated in the bulleted list.

Since your decompressor's capabilities depend on the full combination of parameters, it must inspect all the relevant parameters before indicating that it will perform one of the operations itself. For instance, if your decompressor has hardware that can perform scaling only if the destination pixel depth is 32 and there is no clipping, then the pre-decompression operation would have to check the following fields in the decompression parameters structure: the matrix field, the pixelSize field of the destination pixel map structure pointed to by the destPixMap field, and the maskBits fields. Only then could the decompressor decide whether to set the codecCanScale flag in the capabilities field of the decompression parameters structure.

Listing 4-3 Preparing for simple decompression

pascal long 
CDPreDecompress(Handle storage, register CodecDecompressParams *p)
{
   register CodecCapabilities*capabilities = p->capabilities;
   RectdRect = p->srcRect;
   
   /* 
      Check if the matrix is OK for this decompressor. 
      This decompressor doesn't do anything fancy. 
   */
   
   if ( !TransformRect(p->matrix,&dRect,nil) )
      return(codecConditionErr);

   /* 
      Decide which depth compressed data this decompressor can 
      deal with. 
   */
   
   switch ( (*p->imageDescription)->depth )  {
      case 16:
         break;
      default:
         return(codecConditionErr);
         break;
   }
      /* 
         This decompressor can deal only with 32-bit pixels. 
      */

   capabilities->wantedPixelSize = 32;
   
   /* 
      The smallest possible band the decompressor can handle is 
      2 scan lines. 
   */
   
   capabilities->bandMin = 2;

   /* This decompressor can deal with 2 scan line high bands. */

   capabilities->bandInc = 2;
   
   /* 
      If this decompressor needed its pixels be aligned on 
      some integer multiple, you would set extendWidth and 
      extendHeight to the number of pixels by which you need the
      destination extended. If you don't have such requirements
      or if you take care of them yourself, you set extendWidth
      and extendHeight to 0. 
   */

   capabilities->extendWidth = p->srcRect.right & 1;
   capabilities->extendHeight = p->srcRect.bottom & 1;
   
   return(noErr);
}

Decompressing a Horizontal Band of an Image

Listing 4-4 shows how to decompress the horizontal band of an image. The Image Compression Manager calls the CDBandDecompress function when it wants a decompressor to decompress an image or a horizontal band of an image. The pixel data indicated by the baseAddr field is guaranteed to conform to the criteria your decompressor specified in the CDPreDecompress function.

Note
This example does not perform decompression on bands with a bit depth of more than one or an extension of width and height. If the example did do so, it would handle these cases faster.
Listing 4-4 Performing a decompression operation

pascal long
CDBandDecompress(Handle storage,register CodecDecompressParams *p)
{
   Rect           dRect;
   long           offsetH,offsetV;
   Globals        **glob  = (Globals **)storage;
   long           numLines,numStrips;
   short          rowBytes;
   long           stripBytes;
   short          width;
   register short y;
   register char* baseAddr;
   char           *cDataPtr;
   char           mmuMode = 1;
   OSErr          result = noErr;
   /* 
      Calculate the real base address based on the boundary 
      rectangle. If it's not a linear transformation, this 
      decompressor does not perform the operation. 
*/

   dRect = p->srcRect;
   if ( !TransformRect(p->matrix,&dRect,nil) )
      return(paramErr);

   /* If there is a progress function, give it an open call at
      the start of this band. 
   */

   if (p->progressProcRecord.progressProc)
      p->progressProcRecord.progressProc(codecProgressOpen,0,
         p->progressProcRecord.progressRefCon);
   
   /* 
      Initialize some local variables. 
   */
   
   width = (*p->imageDescription)->width;
   rowBytes = p->dstPixMap.rowBytes;
   numLines = p->stopLine - p->startLine; /* number of scan lines
                                             in this band */
   numStrips = (numLines+1)>>1;           /* number of strips in
                                             this band */
   stripBytes = ((width+1)>>1) * 5;       /* number of bytes in
                                             1 strip of blocks */

   cDataPtr = p->data;
   
   /* 
      Adjust the destination base address to be at the beginning
      of the desired rectangle. 
   */
   
   offsetH = (dRect.left - p->dstPixMap.bounds.left);
   switch (  p->dstPixMap.pixelSize ) {
      case 32:
         offsetH <<=2;  /* 1 pixel = 4 bytes */
         break;
      case 16:
         offsetH <<=1;  /* 1 pixel = 2 bytes */
         break;
      case 8:                 
         break;               /* 1 pixel = 1 byte */
      default:
         result = codecErr;   /* This decompressor doesn't handle
                                 these cases, although it 
                                 could. */
      goto bail;
   }
   offsetV = (dRect.top - p->dstPixMap.bounds.top) * rowBytes;
   baseAddr = p->dstPixMap.baseAddr + offsetH + offsetV;
   /* 
      If your decompressor component is skipping some data, 
      it just skips it here. You can tell because
      firstBandInFrame indicates this is the first band for a new
      frame, and if startLine is not 0, then that many lines were
      clipped out.
    */
   if ( (p->conditionFlags & codecConditionFirstBand) && 
         p->startLine != 0 ) {
      if ( p->dataProcRecord.dataProc ) {
         for ( y=0; y  < p->startLine>>1; y++ )  {
            if ( (result=p->dataProcRecord.dataProc
                   (&cDataPtr,stripBytes,
                  p->dataProcRecord.dataRefCon)) != noErr ) {
               result = codecSpoolErr;
               goto bail;
            }
            cDataPtr += stripBytes;
         }
      } else
         cDataPtr += (p->startLine>>1) * stripBytes;
   }
/* 
   If there is a data-loading function spooling the data to your 
   decompressor, then you have to decompress the data in the
   chunk size that is specified, or, if there is a progress
   function, you must make sure to call it as you go along. 
*/
   if ( p->dataProcRecord.dataProc ||
       p->progressProcRecord.progressProc ) {

      SharedGlobals *sg = (*glob)->sharedGlob;
   
      for (y=0; y < numStrips; y++) {
         if (p->dataProcRecord.dataProc) {
            if ( (result=p->dataProcRecord.dataProc
                   (&cDataPtr,stripBytes,
                  p->dataProcRecord.dataRefCon)) != noErr ) {
               result = codecSpoolErr;
               goto bail;
            }
         }
         SwapMMUMode(&mmuMode);
         DecompressStrip(cDataPtr,baseAddr,rowBytes,width,sg);
         SwapMMUMode(&mmuMode);
         baseAddr += rowBytes<<1;
         cDataPtr += stripBytes;
         if (p->progressProcRecord.progressProc) {
            if ( (result=p->progressProcRecord.progressProc 
                  (codecProgressUpdatePercent,
               FixDiv(y, numStrips),
               p->progressProcRecord.progressRefCon)) != noErr ) {
               result = codecAbortErr;
                goto bail;
            }
         }
      }
   
/* 
   Otherwise, do the fast case.
*/ 
   } else {
      SharedGlobals *sg = (*glob)->sharedGlob;
      shorttRowBytes = rowBytes<<1;
      
      SwapMMUMode(&mmuMode);
      for ( y=numStrips; y--; ) {
         DecompressStrip(cDataPtr,baseAddr,rowBytes,width,sg);
         baseAddr += tRowBytes;
         cDataPtr += stripBytes;
      }
      SwapMMUMode(&mmuMode);
   }
/* 
   IMPORTANT-- Update the pointer to data in the decompression
   parameters structure, so that when your decompressor gets the
   next band, you'll be at the right place in your data.
*/

   p->data = cDataPtr;
   
   if ( p->conditionFlags & codecConditionLastBand ) {
      /* 
         Tie up any loose ends on the last band of the frame. 
      */
   }

bail:
   /*
      If there is a progress function, give it a close call 
      at the end of this band. 
   */

   if (p->progressProcRecord.progressProc)
      p->progressProcRecord.progressProc(codecProgressClose,0,
         p->progressProcRecord.progressRefCon);
   return(result);
}

Subtopics
Performing Image Compression
Choosing a Compressor
Compressing a Horizontal Band of an Image
Decompressing an Image
Choosing a Decompressor
Decompressing a Horizontal Band of an Image

Previous Book Contents Book Index Next

© Apple Computer, Inc.
7 JUL 1996




Navigation graphic, see text links

Main | Top of Section | What's New | Apple Computer, Inc. | Find It | Feedback | Help